/**
 * @file token.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2024-03-30
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef __JSASM_TOKEN_H__
#define __JSASM_TOKEN_H__
#include "src/common/globals.h"
#include "src/base/bit-field.h"
#include "src/base/bounds.h"

#define BINARY_OP_TOKEN_LIST(T, E)                                  \
    E(T, NULLISH, "??", 3)                                          \
    E(T, OR, "||", 4)                                               \
    E(T, AND, "&&", 5)                                              \
    E(T, BIT_OR, "|", 6)                                            \
    E(T, BIT_XOR, "^", 7)                                           \
    E(T, BIT_AND, "&", 8)                                           \
    E(T, SHL, "<<", 11)                                             \
    E(T, SAR, ">>", 11)                                             \
    E(T, SHR, ">>>", 11)                                            \
    E(T, MUL, "*", 13)                                              \
    E(T, DIV, "/", 13)                                              \
    E(T, MOD, "%", 13)                                              \
    E(T, EXP, "**", 14)                                             \
    E(T, ADD, "+", 12)                                              \
    E(T, SUB, "-", 12)

#define EXPAND_BINOP_ASSIGN_TOKEN(T, name, string, precedence)      \
    T(ASSIGN_##name, string "=", 2)

#define EXPAND_BINOP_TOKEN(T, name, string, precedence)             \
    T(name, string, precedence)

#define TOKEN_LIST(T, K)                                            \
    /* BEGIN Template */                                            \
    /* ES6 Template Literals */                                     \
    T(TEMPLATE_SPAN, nullptr, 0)                                    \
    T(TEMPLATE_TAIL, nullptr, 0)                                    \
    /* END Template */                                              \
                                                                    \
    /* BEGIN Property */                                            \
    T(PERIOD, ".", 0)                                               \
    T(LBRACK, "[", 0)                                               \
    /* END Property */                                              \
                                                                    \
    T(QUESTION_PERIOD, "?.", 0)                                     \
    T(LPAREN, "(", 0)                                               \
                                                                    \
    T(RPAREN, ")", 0)                                               \
    T(RBRACK, "]", 0)                                               \
    T(LBRACE, "{", 0)                                               \
    T(COLON, ":", 0)                                                \
    T(ELLIPSIS, "...", 0)                                           \
    T(CONDITIONAL, "?", 3)                                          \
                                                                    \
    T(SEMICOLON, ";", 0)                                            \
    T(RBRACE, "}", 0)                                               \
    T(EOS, "EOS", 0)                                                \
    T(ARROW, "=>", 0)                                               \
                                                                    \
    T(INIT, "=init", 2)                                             \
    T(ASSIGN, "=", 2)                                               \
    BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_ASSIGN_TOKEN)              \
                                                                    \
    T(COMMA, ",", 1)                                                \
    BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_TOKEN)                     \
    T(NOT, "!", 0)                                                  \
    T(BIT_NOT, "~", 0)                                              \
    K(DELETE, "delete", 0)                                          \
    K(TYPEOF, "typeof", 0)                                          \
    K(VOID, "void", 0)                                              \
                                                                    \
    T(INC, "++", 0)                                                 \
    T(DEC, "--", 0)                                                 \
                                                                    \
    T(EQ, "==", 9)                                                  \
    T(EQ_STRICT, "===", 9)                                          \
    T(NE, "!=", 9)                                                  \
    T(NE_STRICT, "!==", 9)                                          \
    T(LT, "<", 10)                                                  \
    T(GT, ">", 10)                                                  \
    T(LTE, "<=", 10)                                                \
    T(GTE, ">=", 10)                                                \
    T(INSTANCEOF, "instanceof", 10)                                 \
    K(IN, "in", 10)                                                 \
                                                                    \
    K(BREAK, "break", 0)                                            \
    K(CASE, "case", 0)                                              \
    K(CATCH, "catch", 0)                                            \
    K(CONTINUE, "continue", 0)                                      \
    K(DEBUGGER, "debugger", 0)                                      \
    K(DEFAULT, "default", 0)                                        \
                                                                    \
    K(DO, "do", 0)                                                  \
    K(ELSE, "else", 0)                                              \
    K(FINALLY, "finally", 0)                                        \
    K(FOR, "for", 0)                                                \
    K(FUNCTION, "function", 0)                                      \
    K(IF, "if", 0)                                                  \
    /* IN */                                                        \
    /* INSTANCEOF */                                                \
    K(NEW, "new", 0)                                                \
    K(RETURN, "return", 0)                                          \
    K(SWITCH, "switch", 0)                                          \
    K(THROW, "throw", 0)                                            \
    K(TRY, "try", 0)                                                \
    K(VAR, "var", 0)                                                \
    K(WHILE, "while", 0)                                            \
    K(WITH, "with", 0)                                              \
    K(NULL_LITERAL, "null", 0)                                      \
    K(TRUE_LITERAL, "true", 0)                                      \
    K(FALSE_LITERAL, "false", 0)                                    \
    T(NUMBER, nullptr, 0)                                           \
    T(SMI, nullptr, 0)                                              \
    T(BIGINT, nullptr, 0)                                           \
    T(STRING, nullptr, 0)                                           \
                                                                    \
    K(SUPER, "super", 0)                                            \
    T(IDENTIFIER, nullptr, 0)                                       \
    K(GET, "get", 0)                                                \
    K(SET, "set", 0)                                                \
    K(ASYNC, "async", 0)                                            \
    K(AWAIT, "await", 0)                                            \
    K(YIELD, "yield", 0)                                            \
    K(LET, "let", 0)                                                \
    K(STATIC, "static", 0)                                          \
    T(FUTURE_STRICT_RESERVED_WORD, nullptr, 0)                      \
    T(ESCAPED_STRICT_RESERVED_WORD, nullptr, 0)                     \
                                                                    \
    K(ENUM, "enum", 0)                                              \
    K(CLASS, "class", 0)                                            \
    K(CONST, "const", 0)                                            \
    K(EXPORT, "export", 0)                                          \
    K(EXTENDS, "extends", 0)                                        \
    K(IMPORT, "import", 0)                                          \
    T(PRIVATE_NAME, nullptr, 0)                                     \
                                                                    \
    T(ILLEGAL, "ILLEGAL", 0)                                        \
    T(ESCAPED_KEYWORD, nullptr, 0)                                  \
    T(WHITESPACE, nullptr, 0)                                       \
    T(UNINITIALIZED, nullptr, 0)                                    \
    T(REGEXP_LITERAL, nullptr, 0)
    
class Token {
    public:
#define T(name, string, precedence) name,
    enum Value : uint8_t { TOKEN_LIST(T, T) NUM_TOKENS };
#undef T

    static const char* Name(Value token) {
        //DCHECK(2);
        return name_[token];
    }

    using IsKeywordBits = base::BitField8<bool, 0, 1>;
    using IsPropertyNameBits = IsKeywordBits::Next<bool, 1>;

    // Predicates
    static bool IsKeyword(Value token) {
        // TODO
        return IsKeywordBits::decode(token_flags[token]);
    }

    static bool IsPropertyName(Value token) {
        return IsPropertyNameBits::decode(token_flags[token]);
    }

    JSASM_INLINE static bool IsValidIdentifier(Value token,
        LanguageMode language_mode, bool is_generator, bool disallow_await) {
        if (JSASM_LIKELY(base::IsInRange(token, IDENTIFIER, ASYNC))) return true;
        return false;
    }

    static bool IsAnyIdentifier(Value token) {
        return base::IsInRange(token, IDENTIFIER, ESCAPED_STRICT_RESERVED_WORD);
    }

    private:
        static const char* const name_[NUM_TOKENS];
        static const char* const string_[NUM_TOKENS];
        static const uint8_t string_length_[NUM_TOKENS];
        static const int8_t precedence_[2][NUM_TOKENS];
        static const uint8_t token_flags[NUM_TOKENS];
};

#endif // __JSASM_TOKEN_H__